#include "Editor.h"
#include <QtGui>
#include "HierarchyTreeModel.h"
#include "AttributeModel.h"
#include "SceneView.h"
#include "SclXWriter.h"
#include "SclXReader.h"

Editor* Editor::s_pIns = 0;

Editor::Editor(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags), m_currentNode(0)
{
	s_pIns = this;

	ui.setupUi(this);
	
	ui.menu_View->addAction(ui.hierarchyView->toggleViewAction());
	ui.menu_View->addAction(ui.attributeView->toggleViewAction());
	ui.menu_View->addAction(ui.objectView->toggleViewAction());

	ui.xEdit->setValidator(new QDoubleValidator());
	ui.yEdit->setValidator(new QDoubleValidator());

	scene = new Scene();

	SceneView* v = new SceneView(centralWidget(), scene);
	ui.editorLayout->addWidget(v);
	setCentralWidget(v);
	sceneView = v;
	ui.hierarchyTree->setModel(scene->treeModel());
	ui.hierarchyTree->setAcceptDrops(true);
	ui.hierarchyTree->setDropIndicatorShown(true);
	
	m_attrModel = new AttributeModel;
	ui.attributeList->setModel(m_attrModel);

	connect(ui.hierarchyTree, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onActivated(const QModelIndex &)));
}

Editor::~Editor()
{
	delete scene;
}

void Editor::selectNode(Node* node, bool clear) {
	if (clear) scene->clearSelection();

	m_currentNode = node;

	m_attrModel->selectNode(node);

	if (node == 0) {
		ui.objectView->setWindowTitle(tr("Object"));
		return;
	}

	node->setSelected(true);

	scene->setCurrentNode(node);

	updateCurrentNodeInfo();
}

void Editor::updateCurrentNodeInfo() {	
	ui.objectNameEdit->setText(m_currentNode->name);

	ui.xEdit->setText(QString::number(m_currentNode->x()));
	ui.yEdit->setText(QString::number(m_currentNode->y()));

	switch(m_currentNode->type()) {
	case Node::NNode:
		ui.objectView->setWindowTitle(tr("Node"));
		break;
	case Node::NSprite:
		ui.objectView->setWindowTitle(tr("Sprite"));
		break;
	case Node::NLayer:
		ui.objectView->setWindowTitle(tr("Layer"));
		break;
	}
}

void Editor::updateNode(Node *node) {
	if (node == m_currentNode) {
		updateCurrentNodeInfo();
	}
	scene->updateNode(node);
}

//slots
void Editor::on_actionSave_triggered() {
	//ui.statusBar->showMessage(QString::number(scene->getRoot()->childCount()));
	QString fileName = QFileDialog::getSaveFileName(this,
										tr("Save Level"),
                                        QDir::currentPath(),
                                        tr("Level Files (*.scl *.xml)"));
	if (fileName.isEmpty())
         return;

	SclXWriter writer(scene);

	QFile file(fileName);
	if (!file.open(QFile::WriteOnly | QFile::Text)) {
		QMessageBox::warning(this, tr("Sand Castle"),
						  tr("Cannot write file %1:\n%2.")
						  .arg(fileName)
						  .arg(file.errorString()));
		return;
    }
	writer.writeFile(&file);
}

void Editor::on_actionLoad_triggered() {
	QString fileName = QFileDialog::getOpenFileName(this,
										tr("Load Level"),
										QDir::currentPath(),
										tr("Level Files (*.scl *.xml)"));
	if (fileName.isEmpty())
		return;
	
	SclXReader reader;

	QFile file(fileName);
	if (!file.open(QFile::ReadOnly | QFile::Text)) {
         QMessageBox::warning(this, tr("Sand Castle"),
                              tr("Cannot read file %1:\n%2.")
                              .arg(fileName)
                              .arg(file.errorString()));
		 return;
	}
	Scene* s = reader.readFile(&file);
	if (!s) {
		QMessageBox::warning(this, tr("Sand Castle"),
							tr("Parse error in file %1:\n\n%2")
							.arg(fileName)
							.arg(reader.errorString()));
		return;
	}
	selectNode(0);
	ui.hierarchyTree->setModel(s->treeModel());
	sceneView->resetScene(s);
	delete scene;
	scene = s;

	//clear history
	Command* c;
	foreach(c, redoQueue) delete c;
	foreach(c, undoQueue) delete c;
	redoQueue.clear();
	undoQueue.clear();
}
void Editor::on_actionExit_triggered() {
	close();
}

void Editor::on_actionUndo_triggered() { undo(); }
void Editor::on_actionRedo_triggered() { redo(); }

void Editor::on_actionEmpty_triggered() {
	invokeNewCommand(new AddNodeCommand(Scene::createEmpty()));//addNode(Scene::createEmpty());
}

void Editor::on_actionLayer_triggered() {
	invokeNewCommand(new AddNodeCommand(Scene::createLayer()));
}

void Editor::on_actionSprite_triggered() {
	invokeNewCommand(new AddNodeCommand(Scene::createSprite()));
}

void Editor::on_actionMoveView_triggered() {
	if (scene->isHandlingAction()) {
		ui.actionMoveView->setChecked(ui.actionMoveView->isChecked());
		return;
	}
	if (ui.actionMoveView->isChecked()) {
		ui.actionScaleView->setChecked(false);
		sceneView->setCurrentTool(SceneView::T_MOVEVIEW);
	} else sceneView->setCurrentTool(SceneView::T_MOVE);
}

void Editor::on_actionScaleView_triggered() {
	if (scene->isHandlingAction()) {
		ui.actionScaleView->setChecked(ui.actionScaleView->isChecked());
		return;
	}
	if (ui.actionScaleView->isChecked()) {
		ui.actionMoveView->setChecked(false);
		sceneView->setCurrentTool(SceneView::T_SCALEVIEW);
	} else sceneView->setCurrentTool(SceneView::T_MOVE);
}

void Editor::on_xEdit_editingFinished() {
	if (!m_currentNode) return;
	invokeNewCommand(new EditNodePropCommand(m_currentNode, EditNodePropCommand::NP_POS,
		QVariant(QPointF(ui.xEdit->text().toDouble(), m_currentNode->y())),
		QVariant(QPointF(m_currentNode->x(), m_currentNode->y()))));
}
void Editor::on_yEdit_editingFinished() {
	if (!m_currentNode) return;
	invokeNewCommand(new EditNodePropCommand(m_currentNode, EditNodePropCommand::NP_POS,
		QVariant(QPointF(m_currentNode->x(), ui.yEdit->text().toDouble())),
		QVariant(QPointF(m_currentNode->x(), m_currentNode->y()))));
}

void Editor::on_scaleXEdit_editingFinished() {
	if (!m_currentNode) return;
	invokeNewCommand(new EditNodePropCommand(m_currentNode, EditNodePropCommand::NP_SCALE,
		QVariant(QPointF(ui.scaleXEdit->text().toDouble(), m_currentNode->scaleY())),
		QVariant(QPointF(m_currentNode->scaleX(), m_currentNode->scaleY()))));
}

void Editor::on_scaleYEdit_editingFinished() {
	if (!m_currentNode) return;
	invokeNewCommand(new EditNodePropCommand(m_currentNode, EditNodePropCommand::NP_SCALE,
		QVariant(QPointF(m_currentNode->scaleX(), ui.scaleYEdit->text().toDouble())),
		QVariant(QPointF(m_currentNode->scaleX(), m_currentNode->scaleY()))));
}

void Editor::addNode(Node* node) {
	scene->addNode(node);
	selectNode(node);
}

void Editor::removeNode(Node* node) {
	scene->removeNode(node);
	if (m_currentNode == node) selectNode(0);
}

void Editor::onActivated(const QModelIndex& index) {
	selectNode(static_cast<Node*>(index.internalPointer()));
}

//name edit
void Editor::on_objectNameEdit_editingFinished(){
	if (m_currentNode == 0 || m_currentNode->name == ui.objectNameEdit->text()) return;
	invokeNewCommand(new EditNodePropCommand(m_currentNode, EditNodePropCommand::NP_NAME, QVariant(ui.objectNameEdit->text()), QVariant(m_currentNode->name)));
}


void Editor::invokeNewCommand(Command* cmd) {
	cmd->execute();
	pushCommand(cmd);
}
void Editor::undo() {
	if (undoQueue.empty()) return;
	undoQueue.back()->undo();
	redoQueue.push_back(undoQueue.back());
	undoQueue.pop_back();
}
void Editor::redo() {
	if (redoQueue.empty()) return;
	redoQueue.back()->execute();
	undoQueue.push_back(redoQueue.back());
	redoQueue.pop_back();
}

void Editor::pushCommand(Command* cmd) {
	undoQueue.push_back(cmd);
	if (undoQueue.size() > 40/*limit*/) { delete undoQueue.front(); undoQueue.pop_front(); }
	Command* c;
	foreach(c, redoQueue) {
		delete c;
	}
	redoQueue.clear();
}


AddNodeCommand::AddNodeCommand(Node * node) : node(node) {
}

void AddNodeCommand::execute() {
	Editor::ins().addNode(node);
}

void AddNodeCommand::undo() {
	Editor::ins().removeNode(node);
}

RemoveNodeCommand::RemoveNodeCommand(const QList<Node*> &nodes) : nodes(nodes), isExecuted(false)
{}

RemoveNodeCommand::~RemoveNodeCommand() {
	if (isExecuted)
		foreach(Node* node, nodes)
			Scene::freeNode(node);
}

void RemoveNodeCommand::execute() {
	Editor& e = Editor::ins();
	foreach(Node* node, nodes)
		e.removeNode(node);
	isExecuted = true;
}

void RemoveNodeCommand::undo() {
	Editor& e = Editor::ins();
	foreach(Node* node, nodes)
		e.addNode(node);
	isExecuted = false;
}

MoveNodeCommand::MoveNodeCommand(Node* node, float sx, float sy, float tx, float ty) : node(node), sx(sx), sy(sy), tx(tx), ty(ty){
}

void MoveNodeCommand::execute() {
	node->setX(tx);
	node->setY(ty);
}

void MoveNodeCommand::undo() {
	node->setX(sx);
	node->setY(sy);
}

EditNodePropCommand::EditNodePropCommand(Node *node, NPROP_TYPE type, QVariant &val, QVariant &preVal)
	: node(node), type(type), val(val), preVal(preVal)
{}

void EditNodePropCommand::execute() {
	switch(type) {
	case NP_NAME:
		node->name = qvariant_cast<QString>(val);
		break;
	case NP_POS:
		{
			QPointF p = qvariant_cast<QPointF>(val);
			node->setPos(p.x(), p.y());		
		}
		break;
	case NP_SCALE:
		{
			QPointF p = qvariant_cast<QPointF>(val);
			node->setScaleX(p.x());
			node->setScaleY(p.y());
		}
		break;
	}
	
	Editor::ins().updateNode(node);
}

void EditNodePropCommand::undo() {
	QVariant& val = preVal;
	switch(type) {
	case NP_NAME:
		node->name = qvariant_cast<QString>(val);
		break;
	case NP_POS:
		{
			QPointF p = qvariant_cast<QPointF>(val);
			node->setPos(p.x(), p.y());
		}
		break;
	case NP_SCALE:
		{
			QPointF p = qvariant_cast<QPointF>(val);
			node->setScaleX(p.x());
			node->setScaleY(p.y());
		}
		break;
	}

	Editor::ins().updateNode(node);
}

ParentCommand::ParentCommand(HierarchyTreeModel *model, Node *node, const QModelIndex &parent, int row, const QModelIndex& origParent, int origRow)
	: model(model), node(node), parent(parent), row(row), origParent(origParent), origRow(origRow)
{}

void ParentCommand::execute() {
	model->removeRow(node->row(), model->parent(model->createIndex(node->row(), 0, node)));
	model->addNode(node, row, parent);
}

void ParentCommand::undo() {
	model->removeRow(node->row(), model->parent(model->createIndex(node->row(), 0, node)));
	model->addNode(node, origRow, origParent);
}

AttributeCommand::AttributeCommand(AttributeModel *model, Node *node, const QString &key, const QVariant &val, const QVariant &preVal)
	: model(model), node(node), key(key), val(val), preVal(preVal)
{}

void AttributeCommand::execute() {
	model->setNodeAttr(node, key, val);
}

void AttributeCommand::undo() {
	if (!preVal.isValid()) {
		model->removeNodeAttr(node, key);
	} else {
		model->setNodeAttr(node, key, preVal);
	}
}

MoveAttributeCommand::MoveAttributeCommand(AttributeModel *model, Node *node, const QString &key, const QString &oldKey)
	: model(model), node(node), key(key), oldKey(oldKey)
{}

void MoveAttributeCommand::execute() {
	model->moveNodeAttr(node, oldKey, key);
}

void MoveAttributeCommand::undo() {
	model->moveNodeAttr(node, key, oldKey);
}

RemoveAttributeCommand::RemoveAttributeCommand(AttributeModel* model, Node* node, int idx, const QString& key, const QVariant& val)
	: model(model), node(node), idx(idx), key(key), val(val)
{}

void RemoveAttributeCommand::execute() {
	model->removeNodeAttrByIndex(node, idx);
}

void RemoveAttributeCommand::undo() {
	model->restoreNodeAttrByIndex(node, idx, key, val);
}
